﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;

namespace Co_location
{
    class BewellSleep
    {
        static SortedDictionary<int, SortedDictionary<DateTime, Location>> user2Locs; // user to his locations(time, loc pair)
        static SortedDictionary<int, List<Sleep>> user2Sleeps;
        static SortedDictionary<int, List<Sleep>> user2Patterns;
        static Dictionary<string, int> gps2newLocID;
        
        static double[] idf;
        static int maxLocID = 0;
        static double[] graph;
        static Dictionary<int, List<int>> userlocmatrix;
        static Dictionary<int, Dictionary<int, double>> WSCSMatrix; // Weighted Spatial Cosine Similarity

        static HashSet<int> G1 = new HashSet<int>() { 22, 26, 30, 12, 27, 43 }, G2 = new HashSet<int>() { 32, 37 }, G3 = new HashSet<int>() { 18, 21, 23, 31, 29, 35, 25, 33, 40 }, G4 = new HashSet<int>() { 34, 36, 41 };
        static Dictionary<int, HashSet<int>> Groups = new Dictionary<int, HashSet<int>>() { { 1, G1 }, { 2, G2 }, { 3, G3 }, { 4, G4 } };

        static double timethrshld = 1;
        static double disthrshld = 0.0001;
        static int maxUserNum = 0;
        static double KnownRatio = 0.75;
        static bool splitCom = false;
        static bool useCoLoc = false;
        static int preRun = 0;

        static Dictionary<int, int> index2id;//user
        static Dictionary<int, int> id2index;//user
        static Dictionary<int, ComFeature> comFeatures;       

        public static void Setup(string[] args)
        {
            KnownRatio = double.Parse(args[0]);
            splitCom = bool.Parse(args[1]);
            if (args.Length > 2)
                useCoLoc = bool.Parse(args[2]);
            if (args.Length > 3)
                preRun = int.Parse(args[3]);
        }

        public static void PreRun()
        {
            id2index = new Dictionary<int, int>();
            index2id = new Dictionary<int, int>();
            ReadLableFeature();
            ReadLoc();
            calcUserLocMatrix();
            calcWSCS();
            calcCoLoc();
            outputArff();
            if (splitCom)
                outputComSplitTruth();
            else
                outputTruth();
            if (preRun == 1)
                outputGraph();
            outputKnownForPreRun(user2Sleeps);
        }

        private static void outputKnownForPreRun(SortedDictionary<int,List<Sleep>> user2instance)
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\sleep-known.csv";
            StreamWriter writer = new StreamWriter(path);

            SortedDictionary<int, List<Sleep>>.Enumerator erator = user2instance.GetEnumerator();           
            int selected = 0;
            while (erator.MoveNext())
            {
                List<Sleep>.Enumerator slps = erator.Current.Value.GetEnumerator();
                int sid = 0;
                while (slps.MoveNext())
                {
                    Sleep slp = slps.Current;

                    if ((preRun + sid) % 2 == 0)
                    {
                        writer.WriteLine(getName(slp.userID, sid) + "," + getSleepLabel(slp.amount));
                        selected++;
                    }
                    sid++;
                }
            }
            writer.Close();
            Console.WriteLine("Knonw Selected : " + selected);
        }

        public static void Run()
        {
            if (preRun > 0)
                PreRun();
            else
            {
                id2index = new Dictionary<int, int>();
                index2id = new Dictionary<int, int>();
                ReadLableFeature();
                ReadLoc();
                calcUserLocMatrix();
                calcWSCS();
                calcCoLoc();
                outputArff();
                if (splitCom)
                    outputComSplitTruth();
                else
                    outputTruth();
                outputMatrix();
                outputGraph();
                outputKnown();
                outputCommuDetec();
            }
        }

        public static void ReadLableFeature()
        {
            string path = "D:\\Pervasive\\Bewell\\sleep_labels_and_features.txt";

            StreamReader reader = new StreamReader(path);
            string line;

            user2Sleeps = new SortedDictionary<int, List<Sleep>>();
            int count = 0;
            while ((line = reader.ReadLine())!=null)
            {
                string[] splits = line.Split(new char[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);

                Sleep slp = new Sleep(splits, line);
                if (slp.amount == 0)
                    continue;

                count++;
                List<Sleep> sleeps;
                if (!user2Sleeps.TryGetValue(slp.userID, out sleeps))
                {
                    sleeps = new List<Sleep>();
                    user2Sleeps.Add(slp.userID, sleeps);
                }
                sleeps.Add(slp);
                user2Sleeps[slp.userID] = sleeps;

            }

            reader.Close();
            Console.WriteLine("Node Number : " + count);
        }

        public static void ReadLoc()
        {
            gps2newLocID = new Dictionary<string, int>();

            string path = "D:\\Pervasive\\Bewell\\location_dump.csv";

            StreamReader reader = new StreamReader(path);
            string line;

            user2Locs = new SortedDictionary<int, SortedDictionary<DateTime,Location>>();
            int count = 0;
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                if (splits.Length != 7)
                    continue;

                Location loc = new Location(splits);
                if (loc.lat == 0 || loc.lon == 0 || !id2index.Keys.Contains(loc.uid))
                    continue;
                loc.uid = id2index[loc.uid];
                count++;

                #region calcLocid
                int latint = (int)Math.Floor(loc.lat / 0.0001);
                int lonint = (int)Math.Floor(loc.lon / 0.0001);
                string lockey = latint.ToString() + lonint.ToString();
                int tmplocid;
                if (!gps2newLocID.TryGetValue(lockey, out tmplocid))
                {
                    gps2newLocID.Add(lockey, maxLocID);
                    maxLocID++;
                }

                loc.newLocid = gps2newLocID[lockey];
                #endregion

                SortedDictionary<DateTime,Location> locs;

                if (!user2Locs.TryGetValue(loc.uid, out locs))
                {
                    locs = new SortedDictionary<DateTime,Location>();
                    user2Locs.Add(loc.uid, locs);
                }
                if (!locs.Keys.Contains(loc.ts))
                {
                    locs.Add(loc.ts, loc);
                    user2Locs[loc.uid] = locs;
                }               
            }
            reader.Close();

            Console.WriteLine("Loc Number: " + count);
        }

        private static void readCommunityFeature()
        {
            comFeatures = new Dictionary<int, ComFeature>();
            string cPath = System.IO.Directory.GetCurrentDirectory() + "\\CommunityFeatures.csv";
            StreamReader reader = new StreamReader(cPath);
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                ComFeature cf = new ComFeature(splits);
                comFeatures.Add(cf.id, cf);
            }
            reader.Close();
        }
        
        private static void outputArff()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\sleep-schema.arff";
            StreamWriter writer = new StreamWriter(path);

            writer.WriteLine("@nodetype Sleep");
            writer.WriteLine("@attribute UID KEY");
            if (splitCom)
                for (int i = 1; i <= 4; i++)
                {
                    writer.WriteLine(string.Format("@attribute uploadData{0} INT", i));
                    writer.WriteLine(string.Format("@attribute stationaryTime{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute silenceTime{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute Between{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute Close{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute EigenVector{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute Pagerank{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute Clustering{0} CONTINUOUS", i));
                }
            else
            {
                writer.WriteLine("@attribute uploadData INT");
                writer.WriteLine("@attribute stationaryTime CONTINUOUS");
                writer.WriteLine("@attribute silenceTime CONTINUOUS");             
            }
            writer.WriteLine("@attribute SleepTime CATEGORICAL");
            writer.WriteLine("@nodedata sleep-truth.csv");
            
            writer.WriteLine();

            writer.WriteLine("@edgetype Linked Sleep Sleep");
            writer.WriteLine("@Reversible");
            writer.WriteLine("@edgedata sleep-link.rn");

            writer.Close();
        }

        private static void outputTruth()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\sleep-truth.csv";
            StreamWriter writer = new StreamWriter(path);
            
            SortedDictionary<int, List<Sleep>>.Enumerator erator = user2Sleeps.GetEnumerator();

            while (erator.MoveNext())
            {
                List<Sleep>.Enumerator slps = erator.Current.Value.GetEnumerator();
                int sid=0;
                while (slps.MoveNext())
                {
                    Sleep slp = slps.Current;
                    try
                    {
                        
                        writer.WriteLine(getName(slp.userID, sid) + "," + slp.uploadData + "," + slp.stationaryTime + "," + slp.silenceTime + "," + getSleepLabel(slp.amount));                        
                        sid++;
                    }
                    catch (Exception)
                    {
                        Console.WriteLine("------- " + index2id[slp.userID]);

                        throw new Exception(index2id[slp.userID].ToString());
                    }
                }
            }
            writer.Close();
        }

        private static void outputComSplitTruth()
        {
            readCommunityFeature();
            string path = System.IO.Directory.GetCurrentDirectory() + "\\sleep-truth.csv";
            StreamWriter writer = new StreamWriter(path);

            SortedDictionary<int, List<Sleep>>.Enumerator erator = user2Sleeps.GetEnumerator();

            while (erator.MoveNext())
            {
                List<Sleep>.Enumerator slps = erator.Current.Value.GetEnumerator();
                int sid = 0;
                while (slps.MoveNext())
                {
                    Sleep slp = slps.Current;
                    writer.Write(getName(slp.userID, sid) + ",");
                    for (int community = 1; community <= 4; community++)
                    {
                        if (Groups[community].Contains(index2id[slp.userID]))
                        {
                            writer.Write(slp.uploadData + "," + slp.stationaryTime + "," + slp.silenceTime + ",");
                            outputComFeature(writer, comFeatures[index2id[slp.userID]], true);
                        }
                        else
                        {
                            writer.Write("0,0,0,");
                            outputComFeature(writer, comFeatures[index2id[slp.userID]], false);
                        }
                    }
                    writer.WriteLine(getSleepLabel(slp.amount));
                    sid++;
                }         
            }            
            writer.Close();
        }
                
        private static void outputComFeature(StreamWriter writer, ComFeature cf, bool belong)
        {
            if (belong)
                writer.Write(cf.Betweenness + "," + cf.Closeness + "," + cf.Eigenvector + "," + cf.PageRank + "," + cf.Clustering + ",");
            else
                writer.Write("0,0,0,0,0,");
        }

        private static void outputGraph()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\sleep-link.rn";
            StreamWriter writer = new StreamWriter(path);            
            
            SortedDictionary<int, List<Sleep>>.Enumerator enumOne = user2Sleeps.GetEnumerator();
            int edges = 0;
            while (enumOne.MoveNext())
            {
                SortedDictionary<int, List<Sleep>>.Enumerator enumTwo = user2Sleeps.GetEnumerator();
                int idOne = enumOne.Current.Key;
                while (enumTwo.MoveNext())
                {
                    int idTwo = enumTwo.Current.Key;
                    if (idOne>idTwo)
                        continue;
                    if (useCoLoc)
                    {
                        if (graph[idOne * maxUserNum + idTwo] < 0.00001)
                            continue;
                    }
                    else
                    {
                        if (WSCSMatrix[idOne][idTwo] < 0.00001)
                            continue;
                    }
                    

                    List<Sleep> sleepO = enumOne.Current.Value;
                    List<Sleep> sleepT = enumTwo.Current.Value;

                    for (int i = 0; i < sleepO.Count; i++)
                    {
                        for (int j = 0; j < sleepT.Count; j++)
                        {
                            if (idOne == idTwo && i >= j)
                                continue;
                           
                            if (useCoLoc)
                                writer.WriteLine(getName(idOne, i) + "," + getName(idTwo, j) + "," + graph[idOne * maxUserNum + idTwo].ToString("0.0000"));
                            else
                                writer.WriteLine(getName(idOne, i) + "," + getName(idTwo, j) + "," + WSCSMatrix[idOne][idTwo].ToString("0.00000"));
                            edges++;
                        }
                    }
                }
            }
            writer.Close();
            Console.WriteLine("Edge Number : " + edges);
        }

        private static void outputKnown()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\sleep-known.csv";
            StreamWriter writer = new StreamWriter(path);

            SortedDictionary<int, List<Sleep>>.Enumerator erator = user2Sleeps.GetEnumerator();
            Random rand = new Random();
            int selected = 0;
            while (erator.MoveNext())
            {
                List<Sleep>.Enumerator slps = erator.Current.Value.GetEnumerator();
                int sid = 0;
                while (slps.MoveNext())
                {
                    Sleep slp = slps.Current;

                    if (rand.NextDouble() <= KnownRatio)
                    {
                        writer.WriteLine(getName(slp.userID, sid) + "," + getSleepLabel(slp.amount));
                        selected++;
                    }
                    sid++;
                }
            }
            writer.Close();
            Console.WriteLine("Knonw Selected : " + selected);
        }

        private static void outputMatrix()
        {
            StreamWriter writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\sleep-matrix.csv");
            //writer.WriteLine("Weighted Spacial Consine Similarity Matrix ");
            foreach (int index in WSCSMatrix.Keys)
                writer.Write(",U" + index2id[index]);
            writer.WriteLine();

            foreach (int i in WSCSMatrix.Keys)
            {
                writer.Write("U" + index2id[i]);
                foreach (int j in WSCSMatrix[i].Keys)
                    writer.Write("," + WSCSMatrix[i][j].ToString("0.000"));

                writer.WriteLine();
            }

            writer.WriteLine();
            writer.WriteLine("Co-Loc Ratio Matrix ");
            for (int i = 0; i < maxUserNum; i++)
            {
                for (int j = 0; j < maxUserNum; j++)
                {
                    writer.Write(graph[i * maxUserNum + j].ToString("0.000") + ",");
                }
                writer.WriteLine();
            }
            writer.Close();

            outputCommunity(G1, "G1");
            outputCommunity(G2, "G2");
            outputCommunity(G3, "G3");
            outputCommunity(G4, "G4");                       
        }

        private static void outputCommunity(HashSet<int> group, string groupname)
        {
            StreamWriter writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\" + groupname + ".csv");
            foreach (int index in WSCSMatrix.Keys)
                if (group.Contains(index2id[index]))
                    writer.Write(",U" + index2id[index]);
            writer.WriteLine();

            foreach (int i in WSCSMatrix.Keys)
            {
                if (group.Contains(index2id[i]))
                {
                    writer.Write("U" + index2id[i]);
                    foreach (int j in WSCSMatrix[i].Keys)
                        if (group.Contains(index2id[j]))
                            writer.Write("," + WSCSMatrix[i][j].ToString("0.000"));

                    writer.WriteLine();
                }
            }
            writer.Close();
        }

        private static void outputCommuDetec()
        {
            string gPath = System.IO.Directory.GetCurrentDirectory() + "\\sleep-graph.txt";
            StreamWriter writer = new StreamWriter(gPath);
            foreach (int userA in WSCSMatrix.Keys)
                foreach (int userB in WSCSMatrix.Keys)
                {
                    if (WSCSMatrix[userA][userB] < 0.00005)
                        continue;

                    writer.WriteLine(userA + " " + userB);
                }
            writer.Close();

            string wPath = System.IO.Directory.GetCurrentDirectory() + "\\sleep-weight.weights";
            writer = new StreamWriter(wPath);
            foreach (int userA in WSCSMatrix.Keys)
                foreach (int userB in WSCSMatrix.Keys)
                {
                    if (WSCSMatrix[userA][userB] < 0.00005)
                        continue;

                    writer.WriteLine(userA + " " + userB + " " + WSCSMatrix[userA][userB].ToString("0.0000"));
                }
            writer.Close();

            //writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\mmmmmmmmm.txt");
            //for (int i = 0; i < maxIndex; i++)
            //    writer.WriteLine(i + " " + index2id[i]);
            //writer.Close();                
        }
        
        private static void calcCoLoc()
        {
            graph = new double[maxUserNum * maxUserNum];
            for (int i = 0; i < graph.Length; i++)            
                graph[i] = 0;
            for (int i = 0; i < maxUserNum; i++)
                graph[i * maxUserNum + i] = 1;
            

            SortedDictionary<int, SortedDictionary<DateTime, Location>>.Enumerator erator = user2Locs.GetEnumerator();

            while (erator.MoveNext())
            {
                SortedDictionary<int, SortedDictionary<DateTime, Location>>.Enumerator eratorT = user2Locs.GetEnumerator();
                if (!user2Sleeps.Keys.Contains(erator.Current.Key))
                    continue;
                while (eratorT.MoveNext())
                {
                    if (!user2Sleeps.Keys.Contains(eratorT.Current.Key))
                        continue;
                    if (erator.Current.Key == eratorT.Current.Key)
                        continue;
                    
                    SortedDictionary<DateTime, Location>.Enumerator locs = erator.Current.Value.GetEnumerator();
                    SortedDictionary<DateTime, Location>.Enumerator locsT = eratorT.Current.Value.GetEnumerator();

                    bool lomove = locs.MoveNext(), ltmove = locsT.MoveNext();
                    double colocNum = 0;
                    double cotimeNum = 0;
                    while (lomove && ltmove)
                    {
                        TimeSpan dur = locs.Current.Key - locsT.Current.Key;
                        double durhours = dur.TotalHours;

                        if (Math.Abs(durhours) < timethrshld)
                        {
                            cotimeNum++;

                            double dis = CalcDistance(locs.Current.Value, locsT.Current.Value);

                            if (dis < disthrshld)
                                colocNum++;

                            lomove = locs.MoveNext();
                            ltmove = locsT.MoveNext();
                        }
                        else if (durhours > 0)
                            ltmove = locsT.MoveNext();
                        else if (durhours < 0)
                            lomove = locs.MoveNext();
                    }
                    //graph[erator.Current.Key * maxUserNum + eratorT.Current.Key] = 2 * colocNum / (double)(erator.Current.Value.Count + eratorT.Current.Value.Count);
                    //graph[eratorT.Current.Key * maxUserNum + erator.Current.Key] = 2 * colocNum / (double)(erator.Current.Value.Count + eratorT.Current.Value.Count);
                    graph[erator.Current.Key * maxUserNum + eratorT.Current.Key] = cotimeNum == 0 ? 0 : colocNum / cotimeNum;
                    graph[eratorT.Current.Key * maxUserNum + erator.Current.Key] = cotimeNum == 0 ? 0 : colocNum / cotimeNum;

                }
            }
        }

        private static double CalcDistance(Location l1, Location l2)
        {
            return Math.Sqrt((l1.lat - l2.lat) * (l1.lat - l2.lat) + (l1.lon - l2.lon) * (l1.lon - l2.lon));
        }

        private static string getName(int userID, int SleepID)
        {
            return index2id[userID] + "F" + SleepID;
        }

        private static string getSleepLabel(int amount)
        {
            if (amount <= 3)
                return "short";
            else if (amount <= 6)
                return "mid";
            else if (amount <= 9)
                return "proper";
            else
                return "long";            
        }

        private static void calcUserLocMatrix()
        {
            userlocmatrix = new Dictionary<int, List<int>>();
            foreach (int user in user2Locs.Keys)
            {
                List<int> locnum = new List<int>();
                for (int i = 0; i < maxLocID; i++)
                {
                    locnum.Add(0);
                }                
                foreach (Location loc in user2Locs[user].Values)
                {
                    try
                    {
                        locnum[loc.newLocid]++;
                    }
                    catch (Exception)
                    {
                        throw new Exception(loc.newLocid + " " + maxLocID);
                    }
                }
                userlocmatrix.Add(user, locnum);
            }
        }

        private static void calcWSCS()
        {
            // Weighted Spatial Cosine Similarity
            #region calc idf ( l )
            idf = new double[maxLocID];
            double D = userlocmatrix.Keys.Count;
            for (int i = 0; i < maxLocID; i++)
            {
                double div = 0;
                foreach (int user in userlocmatrix.Keys)
                {
                    if (userlocmatrix[user][i] != 0)
                        div++;
                }
                idf[i] = Math.Log(D / div);
            }
            #endregion

            WSCSMatrix = new Dictionary<int, Dictionary<int, double>>();
            foreach (int usera in userlocmatrix.Keys)
            {
                List<double> vectora = new List<double>();
                for (int i = 0; i < maxLocID; i++)
                    vectora.Add(userlocmatrix[usera][i] * idf[i]);

                WSCSMatrix.Add(usera, new Dictionary<int, double>());
                foreach (int userb in userlocmatrix.Keys)
                {
                    List<double> vectorb = new List<double>();
                    for (int i = 0; i < maxLocID; i++)
                        vectorb.Add(userlocmatrix[userb][i] * idf[i]);

                    WSCSMatrix[usera].Add(userb, cosineSimilarity(vectora, vectorb));
                }
                WSCSMatrix[usera][usera] = 0;
            }

            string path = System.IO.Directory.GetCurrentDirectory() + "\\WSCS.csv";
            StreamWriter writer = new StreamWriter(path);
            foreach (int usera in WSCSMatrix.Keys)
            {           
                foreach (int userb in WSCSMatrix.Keys)
                {
                    writer.Write(WSCSMatrix[usera][userb].ToString("0.000") + ",");                        
                }
                writer.WriteLine();
            }

           //for (int i = 0; i < maxLocID; i++)
           //     writer.Write(idf[i] + ",");

           // writer.WriteLine();
           // foreach (int user in userlocmatrix.Keys)
           // {
           //     foreach (int num in userlocmatrix[user])
           //     {
           //         writer.Write(num + ",");
           //     }
           //     writer.WriteLine();
           // }
           writer.Close();
        }

        private static double cosineSimilarity(List<double> veca,List<double> vecb)
        {
            double scalea = 0, scaleb = 0;
            double crossproduct = 0;
            for (int i = 0; i < veca.Count; i++)
            {
                crossproduct += veca[i] * vecb[i];

                scalea += veca[i] * veca[i];
                scaleb += vecb[i] * vecb[i];
            }
            return crossproduct / (Math.Sqrt(scalea) * Math.Sqrt(scaleb));            
        }

        #region Pattern

        public static void PatternRun()
        {
            if (preRun > 0)
            {
                id2index = new Dictionary<int, int>();
                index2id = new Dictionary<int, int>();
                ReadLableFeature();
                outputArff();
                generatePatternList();
                outputPatternTruth();
                if (preRun == 1)
                {
                    ReadLoc();
                    calcUserLocMatrix();
                    calcWSCS();
                    calcCoLoc();
                    outputPatternGraph();
                }
                outputKnownForPreRun(user2Patterns);
            }
            else
            {
                id2index = new Dictionary<int, int>();
                index2id = new Dictionary<int, int>();
                ReadLableFeature();
                ReadLoc();
                calcUserLocMatrix();
                calcWSCS();
                calcCoLoc();
                outputArff();
                generatePatternList();
                outputPatternTruth();
                outputPatternKnown();
            }
        }

        private static void generatePatternList()
        {
            user2Patterns = new SortedDictionary<int, List<Sleep>>();
            foreach (int user in user2Sleeps.Keys)
            {
                user2Patterns.Add(user, new List<Sleep>());
                List<Sleep> slps = user2Sleeps[user];
                
                for (int i = 0; i < slps.Count; i +=3)
                {
                    List<Sleep> tmpsleeps = new List<Sleep>();
                    tmpsleeps.Add(slps[i]);
                    if (i + 1 < slps.Count) tmpsleeps.Add(slps[i + 1]);
                    if (i + 2 < slps.Count) tmpsleeps.Add(slps[i + 2]);
                    user2Patterns[user].Add(new Sleep(tmpsleeps));
                }
            }
        }

        private static void outputPatternTruth()
        {
            if (splitCom)
            {
                readCommunityFeature();
            }

            string path = System.IO.Directory.GetCurrentDirectory() + "\\sleep-truth.csv";
            StreamWriter writer = new StreamWriter(path);

            SortedDictionary<int, List<Sleep>>.Enumerator erator = user2Patterns.GetEnumerator();

            while (erator.MoveNext())
            {
                List<Sleep> slps = erator.Current.Value;
                int sid = 0;

                for (int i = 0; i < slps.Count; i++)
                {
                    Sleep slp = slps[i];

                    writer.Write(getName(slp.userID, sid) + ",");

                    if (splitCom)
                        for (int community = 1; community <= 4; community++)
                        {
                            if (Groups[community].Contains(index2id[slp.userID]))
                            {
                                writer.Write(slp.uploadData + "," + slp.stationaryTime + "," + slp.silenceTime + ",");
                                outputComFeature(writer, comFeatures[index2id[slp.userID]], true);
                            }
                            else
                            {
                                writer.Write("0,0,0,");
                                outputComFeature(writer, comFeatures[index2id[slp.userID]], false);
                            }
                        }
                    else
                        writer.Write(slp.uploadData + "," + slp.stationaryTime + "," + slp.silenceTime + ",");
                    writer.WriteLine(getSleepLabel(slp.amount));
                    sid++;
                }
            }
            writer.Close();
        }

        private static void outputPatternGraph()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\sleep-link.rn";
            StreamWriter writer = new StreamWriter(path);

            SortedDictionary<int, List<Sleep>>.Enumerator enumOne = user2Patterns.GetEnumerator();
            int edges = 0;
            while (enumOne.MoveNext())
            {
                SortedDictionary<int, List<Sleep>>.Enumerator enumTwo = user2Patterns.GetEnumerator();
                int idOne = enumOne.Current.Key;
                while (enumTwo.MoveNext())
                {
                    int idTwo = enumTwo.Current.Key;
                    if (idOne > idTwo)
                        continue;
                    if (useCoLoc)
                    {
                        if (graph[idOne * maxUserNum + idTwo] < 0.00001)
                            continue;
                    }
                    else
                    {
                        if (WSCSMatrix[idOne][idTwo] < 0.00001)
                            continue;
                    }


                    List<Sleep> sleepO = enumOne.Current.Value;
                    List<Sleep> sleepT = enumTwo.Current.Value;

                    for (int i = 0; i < sleepO.Count; i ++)
                    {
                        for (int j = 0; j < sleepT.Count; j ++)
                        {
                            if (idOne == idTwo && i >= j)
                                continue;

                            if (useCoLoc)
                                writer.WriteLine(getName(idOne, i) + "," + getName(idTwo, j) + "," + graph[idOne * maxUserNum + idTwo].ToString("0.0000"));
                            else
                                writer.WriteLine(getName(idOne, i) + "," + getName(idTwo, j) + "," + WSCSMatrix[idOne][idTwo].ToString("0.00000"));
                            edges++;
                        }
                    }
                }
            }
            writer.Close();
            Console.WriteLine("Edge Number : " + edges);
        }

        private static void outputPatternKnown()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\sleep-known.csv";
            StreamWriter writer = new StreamWriter(path);

            SortedDictionary<int, List<Sleep>>.Enumerator erator = user2Patterns.GetEnumerator();
            Random rand = new Random();
            int selected = 0;
            while (erator.MoveNext())
            {
                List<Sleep>.Enumerator slps = erator.Current.Value.GetEnumerator();
                int sid = 0;
                while (slps.MoveNext())
                {
                    Sleep slp = slps.Current;

                    if (rand.NextDouble() <= KnownRatio)
                    {
                        writer.WriteLine(getName(slp.userID, sid) + "," + getSleepLabel(slp.amount));
                        selected++;
                    }
                    sid++;
                }
            }
            writer.Close();
            Console.WriteLine("Knonw Selected : " + selected);
        }
        
        #endregion

        struct Location
        {
            public int lid;
            public int uid;
            public int type;

            public double lat;
            public double lon;

            public DateTime ts;

            public int newLocid;

            public Location(string[] splits)
            {
                lid = int.Parse(splits[0]);
                uid = int.Parse(splits[1]);
                //int tmp = int.Parse(splits[1]);
                //if (!id2index.TryGetValue(tmp, out uid))
                //{
                //    uid = maxIndex;
                //    id2index.Add(tmp, uid);
                //    maxIndex++;
                //}

                type = int.Parse(splits[2]);

                lat = double.Parse(splits[3]);
                lon = double.Parse(splits[4]);

                string[] dt = splits[5].Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                string[] date = dt[0].Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
                string[] time = dt[1].Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                try
                {
                    string[] seconds = time[2].Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                    int millis;
                    if (seconds.Length == 2)
                        millis = int.Parse(seconds[1]);
                    else
                        millis = 0;

                    ts = new DateTime(int.Parse(date[0]), int.Parse(date[1]), int.Parse(date[2]), int.Parse(time[0]), int.Parse(time[1]), int.Parse(seconds[0]), millis);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    Console.WriteLine(splits[5]);
                    throw ex;
                }

                newLocid = -1;
            }
        }

        struct Sleep
        {
            public int userID;
            public int amount;
            public int uploadData;
            public double stationaryTime;
            public double silenceTime;

            public Sleep(string[] splits, string line)
            {
                try
                {
                    int tmp = int.Parse(splits[0]);
                    if (!id2index.TryGetValue(tmp, out userID))
                    {
                        userID = maxUserNum;
                        maxUserNum++;
                        id2index.Add(tmp, userID);
                        index2id.Add(userID, tmp);
                    }
                        
                    amount = int.Parse(splits[1]);
                    uploadData = int.Parse(splits[2]);
                    stationaryTime = double.Parse(splits[3]);
                    silenceTime = double.Parse(splits[4]);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("L " + line);
                    Console.WriteLine(splits[0]);
                    throw ex;
                }
            }

            public Sleep(List<Sleep> slps)
            {
                userID = 0;
                amount = 0;
                uploadData = 0;
                stationaryTime = 0;
                silenceTime = 0;
                foreach (Sleep s in slps)
                {
                    userID = s.userID;
                    amount += s.amount;
                    uploadData += s.uploadData;
                    stationaryTime += s.stationaryTime;
                    silenceTime += s.silenceTime;
                }
                amount /= slps.Count;
            }
        }

        struct ComFeature
        {
            public int id;
            public double Betweenness;
            public double Closeness;
            public double Eigenvector;
            public double PageRank;
            public double Clustering;

            public ComFeature(string[] splits)
            {
                id = int.Parse(splits[0]);
                Betweenness = double.Parse(splits[1]);
                Closeness = double.Parse(splits[2]);
                Eigenvector = double.Parse(splits[3]);
                PageRank = double.Parse(splits[4]);
                Clustering = double.Parse(splits[5]);
            }
        }

        #region Label Correlation

        static Dictionary<string, int> allLabels = new Dictionary<string, int>() { { "short", 0 }, { "mid", 1 }, { "proper", 2 }, { "long", 3 } };

        static Dictionary<int, Dictionary<int, double>> cosDis;
        static Dictionary<int, Dictionary<int, double>> eucLid;

        static Dictionary<int, Dictionary<int, int>> label_tf;

        static Dictionary<int, double> label_idf;
        static Dictionary<int, Dictionary<int, double>> label_wscsmatrix;

        public static void OutputLabelCor()
        {
            id2index = new Dictionary<int, int>();
            index2id = new Dictionary<int, int>();
            ReadLableFeature();
            ReadLoc();
            calcUserLocMatrix();
            calcWSCS();
            calcCoLoc();

            getLabelTF();
            calcLabelWSCS();

            /*StreamWriter writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\LabelCorrelation.txt");
            foreach (int userA in user2Sleeps.Keys)
            {
                foreach (int userB in user2Sleeps.Keys)
                {
                    if (userA >= userB || WSCSMatrix[userA][userB] == 0)
                        continue;
                    writer.WriteLine(WSCSMatrix[userA][userB].ToString("0.00000") + " " + label_wscsmatrix[userA][userB].ToString("0.00000"));
                }
            }
            writer.Close();*/
            calcCosDis();
            calcEuc();
            General.OutputLabelCor(WSCSMatrix, label_wscsmatrix);
            General.OutputFivePercent(label_wscsmatrix);
            General.OutputAverage(WSCSMatrix, label_wscsmatrix);
            General.OutputAvgEuc(eucLid, index2id);
        }

        private static void getLabelTF()
        {
            label_tf = new Dictionary<int, Dictionary<int, int>>();
            foreach (var us in user2Sleeps)
            {
                label_tf.Add(us.Key, new Dictionary<int, int>());
                foreach (var l in allLabels)
                    label_tf[us.Key].Add(l.Value, 0);

                foreach (Sleep slp in us.Value)
                    label_tf[us.Key][allLabels[getSleepLabel(slp.amount)]]++;
            }
        }

        private static void calcCosDis()
        {
            cosDis = new Dictionary<int, Dictionary<int, double>>();

            foreach (int userA in label_tf.Keys)
            {
                cosDis.Add(userA, new Dictionary<int, double>());

                foreach (int userB in label_tf.Keys)
                {
                    double dotProduct = 0;
                    double magA = 0, magB = 0;
                    foreach (var pair in label_tf[userA])
                    {
                        dotProduct += pair.Value * label_tf[userB][pair.Key];
                        magA += pair.Value * pair.Value;
                        magB += label_tf[userB][pair.Key] * label_tf[userB][pair.Key];
                    }
                    cosDis[userA].Add(userB, dotProduct / Math.Sqrt(magA * magB));
                }
            }
        }

        private static void calcLabelWSCS()
        {
            #region idf
            
            label_idf = new Dictionary<int, double>();

            foreach (var l in allLabels)
            {
                double div = 0;
                foreach (int user in label_tf.Keys)
                {
                    if (label_tf[user][l.Value] != 0)
                        div++;
                }
                label_idf.Add(l.Value, Math.Log((double)maxUserNum / div));
            }            
            #endregion

            #region label wscs

            label_wscsmatrix = new Dictionary<int, Dictionary<int, double>>();            
            foreach (int usera in label_tf.Keys)
            {
                List<double> vectora = new List<double>();
                foreach(int i in allLabels.Values)
                    vectora.Add(label_tf[usera][i] * label_idf[i]);

                label_wscsmatrix.Add(usera, new Dictionary<int, double>());
                foreach (int userb in label_tf.Keys)
                {
                    List<double> vectorb = new List<double>();
                    foreach(int i in allLabels.Values)
                        vectorb.Add(label_tf[userb][i] * label_idf[i]);

                    label_wscsmatrix[usera].Add(userb, cosineSimilarity(vectora, vectorb));
                }
                label_wscsmatrix[usera][usera] = 0;
            }
            #endregion 
        }

        private static void calcEuc()
        {
            eucLid = new Dictionary<int, Dictionary<int, double>>();

            foreach (int userA in label_tf.Keys)
            {
                eucLid.Add(userA, new Dictionary<int, double>());

                foreach (int userB in label_tf.Keys)
                {
                    double sum = 0;                    
                    foreach (var pair in label_tf[userA])
                    {
                        sum += (pair.Value - label_tf[userB][pair.Key]) * (pair.Value - label_tf[userB][pair.Key]);
                    }
                    eucLid[userA].Add(userB, Math.Sqrt(sum));
                }
            }
        }
        
        #endregion
    }    
}
